package com.tuanzi.loan.business.service.sms.impl;

import com.base.common.http.HttpClientUtil;
import com.base.common.http.model.HttpResult;
import com.base.common.http.model.PostData;
import com.base.common.http.model.RequestHeader;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.tuanzi.loan.business.constants.sms.SmsCalculateMode;
import com.tuanzi.loan.business.constants.sms.SmsPhoneSource;
import com.tuanzi.loan.business.entity.CurrentUser;
import com.tuanzi.loan.business.entity.sms.*;
import com.tuanzi.loan.business.repository.sms.LoanMobnumApiScoreRepository;
import com.tuanzi.loan.business.repository.sms.SmsCalculateTaskRepository;
import com.tuanzi.loan.business.repository.sms.SmsSendTaskRepository;
import com.tuanzi.loan.business.repository.sms.SmsTaskNumberRepository;
import com.tuanzi.loan.business.service.sms.SmsCalculateTaskService;
import com.tuanzi.loan.business.service.sms.el.*;
import com.tuanzi.loan.business.service.sms.vo.SmsSearchResponse;
import com.tuanzi.loan.core.aspect.Debug;
import com.tuanzi.loan.core.exception.SystemException;
import com.tuanzi.loan.core.service.CommonServiceImpl;
import com.tuanzi.loan.core.util.GsonUtils;
import com.tuanzi.loan.core.util.ThrowableUtils;
import com.tuanzi.loan.core.vo.PageSearcher;
import com.tuanzi.loan.vo.request.family.SmsCalculateTaskSearchRequest;
import com.tuanzi.loan.vo.request.family.SmsCondition;
import com.tuanzi.loan.vo.request.family.SmsUserDefinitionRequest;
import com.tuanzi.loan.vo.request.family.sms.ExternalSmsCalculateCondition;
import com.tuanzi.loan.vo.request.family.sms.XiongHuaHuaCheckUserRequest;
import com.tuanzi.loan.vo.response.family.SmsCalculateTaskSearchResponse;
import com.tuanzi.loan.vo.response.family.sms.LoanMobnumApiScoreResponse;
import com.tuanzi.loan.vo.response.family.sms.XiongHuaHuaCheckUserResponse;
import com.tuanzi.server.loan_base.util.BirthdayUtils;
import com.tuanzi.server.loan_base.util.DateUtils;
import com.tuanzi.server.loan_base.util.IdcardValidatorUtil;
import com.tuanzi.server.loan_base.util.phone.PhoneNumberUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.entity.ContentType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.retry.RetryException;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author Ray 2017/09/21
 */
@Debug
@Slf4j
@Service
@Transactional
@Retryable(value = { RetryException.class })
public class SmsCalculateTaskServiceImpl extends CommonServiceImpl<SmsCalculateTask, Long> implements SmsCalculateTaskService {

	private static final String XIONG_HUA_HUA_CHECK_USER = "1"; //熊花花撞库用户

	@Autowired
	private SmsCalculateTaskRepository smsCalculateTaskRepository;
	@Autowired
	private SmsSendTaskRepository smsSendTaskRepository;
	@Autowired
	private SmsTaskNumberRepository smsTaskNumberRepository;
	@Autowired
	private LoanMobnumApiScoreRepository loanMobnumApiScoreRepository;

	private static final Map<String /* 用户账户 */, Boolean /* 预估开关 */> ESTIMATE_SWITCH = Maps.newConcurrentMap();

	private final ExpressionEvaluator<List<String /* phone numbers */>> expressionEvaluator = new MatchRuleExpressionEvaluatorImpl(new MatchRuleEvaluationContextBuilderImpl());

	@Value("${xiong-hua-hua-check-user-url}")
	private String host;

	@Override
	protected JpaRepository<SmsCalculateTask, Long> getRepository() {
		return smsCalculateTaskRepository;
	}

	@Override
	public Page<SmsCalculateTaskSearchResponse> list(PageSearcher<SmsCalculateTaskSearchRequest> pageSearcher) {
		return smsCalculateTaskRepository.list(pageSearcher.getSearchCondition(), pageSearcher.getPageable());
	}

	@Override
	public SmsCalculateTask findTopOneByStatus(SmsCalculateTaskStatus status) {
		return smsCalculateTaskRepository.findTopByStatus(status);
	}


	@Override
	public void cancelTask(Long smsCalculateTaskId) {
		SmsCalculateTask smsCalculateTask = smsCalculateTaskRepository.findOne(smsCalculateTaskId);
		smsCalculateTask.setStatus(SmsCalculateTaskStatus.CALCULATE_CANCEL);
		smsCalculateTaskRepository.save(smsCalculateTask);
	}

	@Override
	public void recalculate(Long smsCalculateTaskId) {
		SmsCalculateTask smsCalculateTask = smsCalculateTaskRepository.findOne(smsCalculateTaskId);
		smsCalculateTask.setStatus(SmsCalculateTaskStatus.READY_CALCULATE);
		smsCalculateTaskRepository.save(smsCalculateTask);
	}

	@Override
	public void activeTask(Long smsCalculateTaskId) {
		SmsCalculateTask smsCalculateTask = smsCalculateTaskRepository.findOne(smsCalculateTaskId);
		smsCalculateTask.setStatus(SmsCalculateTaskStatus.READY_CALCULATE);
		smsCalculateTaskRepository.save(smsCalculateTask);
	}

	@Override
	public Long countByStatus(SmsCalculateTaskStatus status) {
		return smsCalculateTaskRepository.countByStatus(status);
	}

	@Override
	public Long countByStatus(List<SmsCalculateTaskStatus> status) {
		return smsCalculateTaskRepository.countByStatusIn(status);
	}

	@Override
	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void updateStatusWithoutTransactional(SmsCalculateTask smsCalculateTask, SmsCalculateTaskStatus status) {
		smsCalculateTask.setStatus(status);
		smsCalculateTaskRepository.save(smsCalculateTask);
	}

	@Override
	public SmsCalculateTask addCalculateTask(SmsCondition condition) {
		SmsCalculateTask task = new SmsCalculateTask();
		task.setName(condition.getName());
		task.setPhoneSource(SmsPhoneSource.INNER);
		task.setCondition(GsonUtils.toJson(condition));
		task.setStatus(SmsCalculateTaskStatus.READY_CALCULATE);
		task.setCreateTime(new Date());
		task.setSentCount(0);
		if(Objects.nonNull(condition.getCalculateMode()) && condition.getCalculateMode() == SmsCalculateMode.ONCE_TIMING_CALCULATE.ordinal()){
			task.setCalculateTime(condition.getCalculateTime());
		}else {
			task.setCalculateTime(new Date());
		}

		return smsCalculateTaskRepository.save(task);
	}

	@Override
	public SmsCalculateTask addExternalCalculateTask(ExternalSmsCalculateCondition condition) {
		SmsCalculateTask task = new SmsCalculateTask();
		task.setName(condition.getName());
		task.setPhoneSource(SmsPhoneSource.OUTER);
		task.setCondition(GsonUtils.toJson(condition));
		task.setStatus(SmsCalculateTaskStatus.READY_CALCULATE);
		task.setCreateTime(new Date());
		task.setSentCount(0);
		task.setCalculateTime(new Date());

		return smsCalculateTaskRepository.save(task);
	}

	@Override
	public void calculateSmsCount(SmsCalculateTask smsCalculateTask) {
		try {

			List<String> phones = null;
			String dsl = null;
			if(smsCalculateTask.getPhoneSource().equals(SmsPhoneSource.INNER)){  //计算内部号码
				SmsCondition request = GsonUtils.toObject(smsCalculateTask.getCondition(), SmsCondition.class);
				//查找剔除日期、剔除甲方对应的计算任务
				List<Long> excludeCalculateTaskIds = null;
				if(request.getExcludeSendedDay() != null && CollectionUtils.isNotEmpty(request.getExcludeSendedFirstPartyNames())) {

					String executeDate = DateUtils.getStartTimeBeforeDays(request.getExcludeSendedDay() - 1);
					excludeCalculateTaskIds = smsSendTaskRepository.findDistinctCalculateTaskIdByFirstPartyNamesAndExecuteTimeAfter(
							DateUtils.formatYyyyMMddHHmmssToDate(executeDate), request.getExcludeSendedFirstPartyNames());
				}else if(request.getExcludeSendedDay() != null) {

					String executeDate = DateUtils.getStartTimeBeforeDays(request.getExcludeSendedDay() - 1);
					excludeCalculateTaskIds = smsSendTaskRepository.findDistinctCalculateTaskIdByStatusAndExecuteTimeAfter(SmsSendTaskStatus.SEND_DONE,
							DateUtils.formatYyyyMMddHHmmssToDate(executeDate));
				}else if(CollectionUtils.isNotEmpty(request.getExcludeSendedFirstPartyNames())){

					excludeCalculateTaskIds = smsSendTaskRepository.findDistinctCalculateTaskIdByFirstPartyNames(request.getExcludeSendedFirstPartyNames());
				}

				request.setExcludeCalculateTaskIds(excludeCalculateTaskIds);
				List<SmsSearchResponse> smsSearchResponses = smsCalculateTaskRepository.listSmsUser(request);
				smsSearchResponses = smsSearchResponses.parallelStream().filter(s -> {
					boolean minAgeFlag = true;
					boolean maxAgeFlag = true;
					boolean sexFlag = true;
					String idcard = s.getIdcard();
					if(StringUtils.isBlank(idcard) || !IdcardValidatorUtil.isValidatedAllIdcard(idcard)){
						return true;
					}else{
						Integer age = BirthdayUtils.calculateAgeFromIdCard(idcard);
						String user_sex = BirthdayUtils.calculateSexFromIdCard(idcard);
						if(request.getMinAge() != null){
							if(age < request.getMinAge()){
								minAgeFlag = false;
							}
						}
						if(request.getMaxAge() != null){
							if(age > request.getMaxAge()){
								maxAgeFlag = false;
							}
						}
						List<String> task_sex = request.getSexs();
						if(task_sex != null && task_sex.size() > 0){
							sexFlag = task_sex.contains(user_sex);
						}
						return (minAgeFlag && maxAgeFlag && sexFlag);
					}
				}).collect(Collectors.toList());

				MatchRuleContext matchRuleContext = new MatchRuleContext(request, smsSearchResponses);
				MatchRuleContext.setMatchRuleContext(matchRuleContext);
				dsl = structureDSL(request);
				phones = expressionEvaluator.evaluate(dsl);


				//剔除熊花花用户
				if(request.getElimilateCheckFirstPartis().contains(XIONG_HUA_HUA_CHECK_USER)){
					log.info("短信计算任务ID：" + smsCalculateTask.getId() + " 调用熊花花撞库前号码包大小：" + phones.size());

					List<String> afterElimilatePhones = eliminateXiongHuaHuaUser(phones);
					if(CollectionUtils.isNotEmpty(afterElimilatePhones)){
						phones.clear();
						phones.addAll(afterElimilatePhones);
						log.info("短信计算任务ID：" + smsCalculateTask.getId() + " 调用熊花花撞库后号码包大小：" + phones.size());
					}
				}


			}else if(smsCalculateTask.getPhoneSource().equals(SmsPhoneSource.OUTER)){ //计算外部号码

				ExternalSmsCalculateCondition externalSmsCalculateCondition = GsonUtils.toObject(smsCalculateTask.getCondition(), ExternalSmsCalculateCondition.class);
				List<LoanMobnumApiScoreResponse> loanMobnumApiScoreResponses = loanMobnumApiScoreRepository.list(externalSmsCalculateCondition);

				phones = loanMobnumApiScoreResponses.stream().map(LoanMobnumApiScoreResponse::getMobnum).collect(Collectors.toList());

				for(LoanMobnumApiScoreResponse response: loanMobnumApiScoreResponses){  //计算次数+1
					MobnumApiScore mobnumApiScore = loanMobnumApiScoreRepository.findOne(response.getId());
					mobnumApiScore.setExportCount(response.getExportCount() == null ? 1 : response.getExportCount() + 1);
					mobnumApiScore.setUpdateTime(new Date());
					loanMobnumApiScoreRepository.save(mobnumApiScore);
				}
			}


			persistSmsTaskNumber(smsCalculateTask.getId(), phones);
			smsCalculateTask.setDslRule(dsl);
			smsCalculateTask.setPhoneCount(phones.size());
			smsCalculateTask.setFinishTime(new Date());
			smsCalculateTask.setStatus(SmsCalculateTaskStatus.CALCULATE_DONE);


		} catch (Throwable e) {
			log.error(e.getMessage(), e);
			smsCalculateTask.setErrorReason(ThrowableUtils.getStackTraceString(e));
			smsCalculateTask.setStatus(SmsCalculateTaskStatus.CALCULATE_FAIL);
		} finally {
			smsCalculateTaskRepository.save(smsCalculateTask);
		}

		// 计算任务计算完毕时,更新发送任务状态为"等待计算完成"的发送任务
		if (smsCalculateTask.getStatus() == SmsCalculateTaskStatus.CALCULATE_DONE) {
			smsSendTaskRepository.findAllByCalculateTaskId(smsCalculateTask.getId()).stream().filter(x -> x.getStatus() == SmsSendTaskStatus.WAITING_CALCULATE_DONE)
					.peek(x -> x.setStatus(SmsSendTaskStatus.READY_SEND)).forEach(smsSendTaskRepository::save);
		}
	}

	@Override
	public Long estimateSmsCount(SmsCondition smsCondition) {
		if (canEstimate() != null && !canEstimate()) {
			throw new SystemException("存在未执行完毕的预估, 请等待上次预估执行完毕");
		}
		closeEstimateSwitch();
		Long estimateCount = smsCalculateTaskRepository.countSmsUser(smsCondition);
		try {
			TimeUnit.SECONDS.sleep(10);
		} catch (Exception e) {

		}
		openEstimateSwitch();
		return estimateCount;
	}

	@Override
	public SmsCalculateTask findTopByStatusAndCalculateTimeBeforeOrderByCalculateTime(SmsCalculateTaskStatus status, Date date) {
		return smsCalculateTaskRepository.findTopByStatusAndCalculateTimeBeforeOrderByCalculateTime(status, date);
	}

	@Override
	public SmsCalculateTask importPhones(String name, List<String> phones) {
		SmsCalculateTask task = new SmsCalculateTask();
		task.setName(name);
		task.setPhoneSource(SmsPhoneSource.IMPORT);
		task.setStatus(SmsCalculateTaskStatus.READY_CALCULATE);
		task.setCreateTime(new Date());
		task.setSentCount(0);
		task.setCalculateTime(new Date());

		//创建计算任务
		task =  smsCalculateTaskRepository.save(task);

		//导入号码
		persistSmsTaskNumber(task.getId(), phones);

		//修改计算任务为计算完成
		task.setStatus(SmsCalculateTaskStatus.CALCULATE_DONE);
		task.setPhoneCount(phones.size());
		task.setFinishTime(new Date());
		task = smsCalculateTaskRepository.save(task);

		return task;

	}

	@Override
	public List<SmsCalculateTask> findAllByIdsIn(List<Long> ids) {
		return smsCalculateTaskRepository.findAllByIdIn(ids);
	}

	private Boolean canEstimate() {
		return ESTIMATE_SWITCH.get(CurrentUser.get().getAccount());
	}

	private void closeEstimateSwitch() {
		ESTIMATE_SWITCH.put(CurrentUser.get().getAccount(), Boolean.FALSE);
	}

	private void openEstimateSwitch() {
		ESTIMATE_SWITCH.put(CurrentUser.get().getAccount(), Boolean.TRUE);
	}


	private String structureDSL(SmsCondition request) {
		List<String> strs = Lists.newArrayList();
		SmsUserDefinitionRequest userDefinitionRequest = request.getUserDefinitionRequest();

		if (userDefinitionRequest != null) {
			if (CollectionUtils.isNotEmpty(userDefinitionRequest.getEliminateActivities())) {
				strs.add(userDefinitionRequest.getEliminateActivities().stream().collect(Collectors.joining(BackToRootEvaluator.BACK_DSL)));
			}

			if (CollectionUtils.isNotEmpty(userDefinitionRequest.getEliminateBlackList())) {
				strs.add(userDefinitionRequest.getEliminateBlackList().stream().collect(Collectors.joining(BackToRootEvaluator.BACK_DSL)));
			}

			if (StringUtils.isNotBlank(userDefinitionRequest.getMinAge())) {
				strs.add("baseInfo.ageGreaterThan(" + userDefinitionRequest.getMinAge() + ")");
			}

			if (StringUtils.isNotBlank(userDefinitionRequest.getMaxAge())) {
				strs.add("baseInfo.ageLessThan(" + userDefinitionRequest.getMaxAge() + ")");
			}

			if (StringUtils.isNotBlank(userDefinitionRequest.getMinSesamePoint())) {
				strs.add("baseInfo.sesamePointGreaterThan(" + userDefinitionRequest.getMinSesamePoint() + ")");
			}

			if (StringUtils.isNotBlank(userDefinitionRequest.getMaxSesamePoint())) {
				strs.add("baseInfo.sesamePointLessThan(" + userDefinitionRequest.getMaxSesamePoint() + ")");
			}

			if (StringUtils.isNotBlank(userDefinitionRequest.getMinIncome())) {
				strs.add("baseInfo.incomeGreaterThan(" + userDefinitionRequest.getMinIncome() + ")");
			}

			if (StringUtils.isNotBlank(userDefinitionRequest.getMaxIncome())) {
				strs.add("baseInfo.incomeLessThan(" + userDefinitionRequest.getMaxIncome() + ")");
			}

			if (StringUtils.isNotBlank(userDefinitionRequest.getMinAmount())) {
				strs.add("baseInfo.amountGreaterThan(" + userDefinitionRequest.getMinAmount() + ")");
			}

			if (StringUtils.isNotBlank(userDefinitionRequest.getMaxAmount())) {
				strs.add("baseInfo.amountLessThan(" + userDefinitionRequest.getMaxAmount() + ")");
			}

			if (CollectionUtils.isNotEmpty(userDefinitionRequest.getCredit())) {
				strs.add("baseInfo.credit({" + structureParameters(userDefinitionRequest.getCredit()) + "})");
			}

			if (CollectionUtils.isNotEmpty(userDefinitionRequest.getSex())) {
				strs.add("baseInfo.sex({" + structureParameters(userDefinitionRequest.getSex()) + "})");
			}

			if (CollectionUtils.isNotEmpty(userDefinitionRequest.getProfession())) {
				strs.add("baseInfo.profession({" + structureParameters(userDefinitionRequest.getProfession()) + "})");
			}

			if (CollectionUtils.isNotEmpty(userDefinitionRequest.getMarriage())) {
				strs.add("baseInfo.marriage({" + structureParameters(userDefinitionRequest.getMarriage()) + "})");
			}

			if (CollectionUtils.isNotEmpty(userDefinitionRequest.getSocialInsurance())) {
				strs.add("baseInfo.socialInsurance({" + structureParameters(userDefinitionRequest.getSocialInsurance()) + "})");
			}

			if (CollectionUtils.isNotEmpty(userDefinitionRequest.getHouseFund())) {
				strs.add("baseInfo.houseFund({" + structureParameters(userDefinitionRequest.getHouseFund()) + "})");
			}

			if (CollectionUtils.isNotEmpty(userDefinitionRequest.getHasCar())) {
				strs.add("baseInfo.car({" + structureParameters(userDefinitionRequest.getHasCar()) + "})");
			}

			if (CollectionUtils.isNotEmpty(userDefinitionRequest.getHasHouse())) {
				strs.add("baseInfo.house({" + structureParameters(userDefinitionRequest.getHasHouse()) + "})");
			}
		}

		String dsl = StringUtils.join(strs.stream().filter(StringUtils::isNotBlank).toArray(), BackToRootEvaluator.BACK_DSL);

		if (StringUtils.isNotBlank(dsl)) {
			dsl += BackToRootEvaluator.BACK_DSL + "distinctGet";
		} else {
			dsl = "distinctGet";
		}

		log.info("DSL : {}", dsl);

		return dsl;
	}

	private String structureParameters(List<String> strs) {
		return strs.stream().flatMap(x -> Stream.of(StringUtils.split(x, ","))).map(x -> "\"" + x + "\"").collect(Collectors.joining(","));
	}

	private void persistSmsTaskNumber(Long smsTaskId, List<String> phones) {
		smsTaskNumberRepository.save(phones.stream().filter(x -> StringUtils.length(x) <= 13).map(x -> {
			SmsTaskNumber smsTaskNumber = new SmsTaskNumber();
			smsTaskNumber.setTaskId(smsTaskId);
			smsTaskNumber.setMobnum(x);
			return smsTaskNumber;
		}).collect(Collectors.toList()));
	}


	/**
	 * 剔除熊花花用户
	 * @param phones
	 * @return
	 */
	private List<String> eliminateXiongHuaHuaUser(List<String> phones){

		List<String> smsTaskNumbers = PhoneNumberUtils.splitPhones(phones, 20000); //一次查2万号码
		List<String> afterElimilatePhones = Lists.newArrayList();
		smsTaskNumbers.stream().forEach(x ->{
			try {

				XiongHuaHuaCheckUserRequest xiongHuaHuaCheckUserRequest = new XiongHuaHuaCheckUserRequest();
				xiongHuaHuaCheckUserRequest.setPhoneList(x);

				RequestHeader requestHeader = new RequestHeader();
				requestHeader.setHeader("Content-Type", ContentType.APPLICATION_JSON.getMimeType());
				PostData postData = new PostData(GsonUtils.toJson(xiongHuaHuaCheckUserRequest));

				HttpResult httpResult = HttpClientUtil.post(requestHeader, host, postData);
				String response = httpResult.getStringData();
				XiongHuaHuaCheckUserResponse xiongHuaHuaCheckUserResponse = GsonUtils.toObject(response, XiongHuaHuaCheckUserResponse.class);
				if(xiongHuaHuaCheckUserResponse.getTotal() != null && xiongHuaHuaCheckUserResponse.getTotal() > 0
						&& CollectionUtils.isNotEmpty(xiongHuaHuaCheckUserResponse.getData())){
					afterElimilatePhones.addAll(xiongHuaHuaCheckUserResponse.getData());
				}

			} catch (Exception e) {
				log.error(e.getMessage());
			}
		});

		return afterElimilatePhones;
	}

}
